﻿import SCUMMCore.Nodes.Node;
import SCUMMCore.CommonFunctions;
/**
 *@author: Pablo Weremczuk  *[SPA]Coleccion de nodos, usados para pathfinding. Provee metodos para la resolución de caminos entre nodos.
 *[EN]Node Collection, used for pathfinding. Provides methods for path resolution between nodes. */

class SCUMMCore.Nodes.NodeCollection {
	private var arrCollection:Array;
	function NodeCollection() {
		arrCollection = new Array();
	}
	function addNode(param_NodeToAdd:Node) {
		if (!hasNode(param_NodeToAdd)) {
			arrCollection[arrCollection.length] = param_NodeToAdd;
		}
	}
	/**
	 *[SPA] Esta funcion devuelve true si el Nodo que se pasa como parametro esta contenido en la coleccion	 */
	function hasNode(param_NodeToCheck:Node):Boolean {
		for (var i = 0; i<arrCollection.length; i++) {
			if (arrCollection[i].getName() == param_NodeToCheck.getName()) {
				return true;
			}
		}
		return false;
	}
	/**
	devuelve el primer nodo de la coleccion
	*/
	function getFirstNode() {
		if (arrCollection.length>0) {
			return arrCollection[0];
		} else {
			return null;
		}
	}
	/*
	devuelve el ultimo nodo de la coleccion
	*/
	function getLastNode():Node {
		if (arrCollection.length>0) {
			return arrCollection[arrCollection.length-1];
		} else {
			return null;
		}
	}
	/*
	devuelve la cantidad de nodos de la coleccion
	*/
	function getCount():Number {
		return arrCollection.length;
	}
	/**
	devuelve el nodo siguiente al nodo pasado como parametro, si no
	hay otro nodo, devuelve false, este método esta penzado para usar
	con un while. Ej:
	myNode = myNodeCollection.getFirstNode();
	while(myNode){
	 	---Hago algo con el nodo---
	myNode = MyNodeCollection.getNextNode(myNode);
	}
	de esta forma se recorre la coleccion
	*/
	function getNextNode(param_NodeToCheck:Node):Node {
		for (var i = 0; i<arrCollection.length; i++) {
			if (param_NodeToCheck.isNode(arrCollection[i])) {
				if (i<(arrCollection.length-1)) {
					return arrCollection[i+1];
				} else {
					return null;
				}
			}
		}
	}
	/**
	devuelve el nodo anterior al nodo pasado como parametro, si no
	hay otro nodo, devuelve false, este método esta penzado para usar
	con un while. Ej:
	myNode = myNodeCollection.getLastNode();
	while(myNode){
	 	---Hago algo con el nodo---
	myNode = MyNodeCollection.getPrevNode(myNode);
	}
	de esta forma se recorre la coleccion
	*/
	function getPrevNode(param_NodeToCheck:Node):Node {
		for (var i = 0; i<arrCollection.length; i++) {
			
			if (param_NodeToCheck.isNode(arrCollection[i])) {
				if (i>0) {
					return arrCollection[i-1];
				} else {
					return null;
				}
			}
		}
	}
	/**
	 esta funcion concatena la coleccion que se pasa como parametro al final
	 de la propia coleccion
	*/
	function appendCollection(param_colToAdd:Array) {
		var myNodo:Node = param_colToAdd.getFirstNode();
		while (myNodo) {
			addNode(myNodo);
			myNodo = param_colToAdd.getNextNode(myNodo);
		}
	}
	/**
	 * devuelve al menor distancia al nodo que se pasa como parametro
	 * @param param_target:Node el nodo al que se busca la mayor cercan�a
	 */
	function getShortestDistance(param_target:Node):Number {
		var myNode:Node = getFirstNode();
		var shortestDistance:Number = CommonFunctions.distancia(myNode, param_target);
		var actualDistance:Number;
		while (myNode) {
			actualDistance = CommonFunctions.distancia(myNode, param_target);
			if (actualDistance<=shortestDistance) {
				shortestDistance = actualDistance;
			}
			myNode = getNextNode(myNode);
		}
		return shortestDistance;
	}
	/**
	 * Obtinene el nodo mas cercano al nodo que se pasa como parametro
	 * @param param_target:Node el nodo al que se busca la mayor cercan�a
	 */
	function getCloserNode(param_target:Node):Node {
		var myNode:Node = getFirstNode();
		var myCloserNode:Node;
		var shortestDistance:Number;
		var actualDistance:Number;
		
		shortestDistance = getShortestDistance(param_target);
		while (myNode) {
			actualDistance = CommonFunctions.distancia(myNode, param_target);
			if (actualDistance<=shortestDistance) {
				shortestDistance = actualDistance;
				myCloserNode = myNode;
			}
			myNode = getNextNode(myNode);
		}
		return myCloserNode;
	}
	/**
	 * Limpia el flag de visitado para todos los nodos
	 */
	function cleanAllVisited() {
		var myNodo:Node = getFirstNode();
		while (myNodo) {
			myNodo.cleanVisited();
			myNodo = getNextNode(myNodo);
		}
	}
	/**
	dibuja las aristas de todos los nodos
	*/
	function drawPaths() {
		var myNode:Node;
		var myNodeTest:Node;
		var arrRelatedNodes:Array = new Array();
		myNode = getFirstNode();
		_root.Scenario.createEmptyMovieClip("particleClip", 100000);
		_root.Scenario.particleClip.clear();
		while (myNode) {
			arrRelatedNodes = myNode.getRelatedNodes();
			for (var i = 0; i<arrRelatedNodes.length; i++) {
				with (_root.Scenario.particleClip) {
					lineStyle(7, 0x000000, 100);
					moveTo(myNode._x, myNode._y);
					lineTo(eval("Scenario.myNodo_"+arrRelatedNodes[i])._x, eval("Scenario.myNodo_"+arrRelatedNodes[i])._y);
				}
			}
			myNode = getNextNode(myNode);
		}
	}
	/*
	*se vale del algoritmo A* para obtener el mejor camino entre los nodos
	*/
	function findPath(param_start:Node, param_end:Node):NodeCollection {
		var myOpenList:NodeCollection = new NodeCollection();
		var myClosedList:NodeCollection = new NodeCollection();
		var myNode:Node;
		var pivoteNode:Node;
		var myNodeToAddOpenToOpenList:Node;
		var distanciaMenor:Number;
		param_start.distancia = CommonFunctions.distancia(param_start, param_end);
		distanciaMenor = param_start.distancia;
		myOpenList.addNode(param_start);
		myNode = myOpenList.getFirstNode();
		while (myNode && !myClosedList.hasNode(param_end)) {
			distanciaMenor = myOpenList.getShortestDistance(param_end);
			myNode.distancia = CommonFunctions.distancia(myNode, param_end);
			if (myNode.distancia<=distanciaMenor) {
				distanciaMenor = myNode.distancia;
				for (var i = 0; i<myNode.getRelatedNodes().length; i++) {
					myNodeToAddOpenToOpenList = getNthNode(myNode.getRelatedNodes()[i]);
					if (!myClosedList.hasNode(myNodeToAddOpenToOpenList) && !myOpenList.hasNode(myNodeToAddOpenToOpenList)) {
						myNodeToAddOpenToOpenList.parent = myNode;
						myOpenList.addNode(myNodeToAddOpenToOpenList);
					}
				}
				pivoteNode = myOpenList.getNextNode(myNode);
				myClosedList.addNode(myNode);
				myOpenList.removeNode(myNode);
				myNode = pivoteNode;
				myNode = myOpenList.getFirstNode();
			} else {
				myNode = myOpenList.getNextNode(myNode);
			}
		}
		var myReturnPath:NodeCollection = new NodeCollection();
		myNode = myClosedList.getLastNode();
		myReturnPath.addNode(myClosedList.getLastNode());
		while (myNode.parent) {
			myReturnPath.addNode(myNode.parent);
			myNode = myNode.parent;
		}
		myReturnPath.addNode(param_end);
		clearParents();
		return myReturnPath;
	}
	/**
	@author: Pablo Weremczuk
	elimina los parents de los nodos, de lo contrario si se vuelve a invocar la rutina de
	findPath entrara en loop
	*/
	function clearParents() {
		for (var i = 0; i<arrCollection.length; i++) {
			arrCollection[i].parent = null;
		}
	}
	function getNthNode(param_nodeIndex:Number):Node {
		if (param_nodeIndex>=0 && param_nodeIndex<=arrCollection.length) {
			return arrCollection[param_nodeIndex];
		} else {
			return null;
		}
	}
	/*
	Devuelve un objeto Node
	*/
	function getNodeByName(param_strName:String):Node {
		for (var i = 0; i<arrCollection.length; i++) {
			if (arrCollection[i].getName() == param_strName) {
				return arrCollection[i];
			}
		}
		return null;
	}
	/*
	Elimina un nodo de la coleccion, true si ok false si no encontro el nodo
	*/
	function removeNode(param_Node:Node):Boolean {
		for (var i = 0; i<arrCollection.length; i++) {
			if (arrCollection[i].isNode(param_Node)) {
				arrCollection.splice(i, 1);
				return true;
			}
		}
		return false;
	}
	function toString():String {
		var arrTemp:String = "";
		for (var i = 0; i<arrCollection.length; i++) {
			arrTemp += arrCollection[i].getMainMovieClip()._name+',';
		}
		return arrTemp;
	}
}
